define(["require", "exports", "vscode-languageclient", "vscode-languageserver-protocol", "vscode-languageserver-protocol", "vscode-css-languageservice", "vscode-languageclient"], function (require, exports, vscode_languageclient_1, vscode_languageserver_protocol_1, vsProto, vscode_css_languageservice_1, vsclient) {
    'use strict';
    Object.defineProperty(exports, "__esModule", { value: true });
    function formatError(message, err) {
        if (err instanceof Error) {
            let error = err;
            return `${message}: ${error.message}\n${error.stack}`;
        }
        else if (typeof err === 'string') {
            return `${message}: ${err}`;
        }
        else if (err) {
            return `${message}: ${err.toString()}`;
        }
        return message;
    }
    function runSafe(func, errorVal, errorMessage, detail) {
        try {
            let t = func();
            if (t instanceof Promise) {
                return t.then((v) => {
                    detail.resolver(v);
                    return void 0;
                }, e => {
                    detail.rejecter(formatError(errorMessage, e));
                    console.error(formatError(errorMessage, e));
                    return errorVal;
                });
            }
            detail.resolver(t);
            return t;
        }
        catch (e) {
            console.error(formatError(errorMessage, e));
            return errorVal;
        }
    }
    function getLanguageModelCache(maxEntries, cleanupIntervalTimeInSec, parse) {
        let languageModels = {};
        let nModels = 0;
        let cleanupInterval = 0;
        if (cleanupIntervalTimeInSec > 0) {
            cleanupInterval = setInterval(() => {
                let cutoffTime = Date.now() - cleanupIntervalTimeInSec * 1000;
                let uris = Object.keys(languageModels);
                for (let uri of uris) {
                    let languageModelInfo = languageModels[uri];
                    if (languageModelInfo.cTime < cutoffTime) {
                        delete languageModels[uri];
                        nModels--;
                    }
                }
            }, cleanupIntervalTimeInSec * 1000);
        }
        return {
            get(document) {
                let version = document.version;
                let languageId = document.languageId;
                let languageModelInfo = languageModels[document.uri];
                if (languageModelInfo && languageModelInfo.version === version && languageModelInfo.languageId === languageId) {
                    languageModelInfo.cTime = Date.now();
                    return languageModelInfo.languageModel;
                }
                let languageModel = parse(document);
                languageModels[document.uri] = { languageModel, version, languageId, cTime: Date.now() };
                if (!languageModelInfo) {
                    nModels++;
                }
                if (nModels === maxEntries) {
                    let oldestTime = Number.MAX_VALUE;
                    let oldestUri = null;
                    for (let uri in languageModels) {
                        let languageModelInfo = languageModels[uri];
                        if (languageModelInfo.cTime < oldestTime) {
                            oldestUri = uri;
                            oldestTime = languageModelInfo.cTime;
                        }
                    }
                    if (oldestUri) {
                        delete languageModels[oldestUri];
                        nModels--;
                    }
                }
                return languageModel;
            },
            onDocumentRemoved(document) {
                let uri = document.uri;
                if (languageModels[uri]) {
                    delete languageModels[uri];
                    nModels--;
                }
            },
            dispose() {
                if (typeof cleanupInterval !== 'undefined') {
                    clearInterval(cleanupInterval);
                    cleanupInterval = void 0;
                    languageModels = {};
                    nModels = 0;
                }
            }
        };
    }
    var vscode;
    function initVsCode() {
        vscode = vscode || (vscode = window["getVsCode"]("css"));
    }
    function asPosition(value) {
        if (!value) {
            return undefined;
        }
        return new vscode.Position(value.line, value.character);
    }
    function asRange(value) {
        if (!value) {
            return undefined;
        }
        return new vscode.Range(asPosition(value.start), asPosition(value.end));
    }
    function activate(context) {
        initVsCode();
        var documents = new Map();
        vscode.workspace.textDocuments.forEach(doc => {
            documents.set(doc.uri.toString(), doc);
        });
        let stylesheets = getLanguageModelCache(10, 60, document => getLanguageService(document).parseStylesheet(document));
        vscode.workspace.onDidCloseTextDocument(doc => {
            stylesheets.onDocumentRemoved(doc);
            documents.delete(doc.uri.toString());
        });
        vscode.workspace.onDidOpenTextDocument(doc => {
            documents.set(doc.uri.toString(), doc);
        });
        let languageServices = {
            css: vscode_css_languageservice_1.getCSSLanguageService(),
            scss: vscode_css_languageservice_1.getSCSSLanguageService(),
            less: vscode_css_languageservice_1.getLESSLanguageService()
        };
        function getLanguageService(document) {
            let service = languageServices[document.languageId];
            if (!service) {
                console.log('Document type is ' + document.languageId + ', using css instead.');
                service = languageServices['css'];
            }
            return service;
        }
        let serverModule = context.asAbsolutePath("");
        let debugOptions = { execArgv: ['--nolazy', '--inspect=6044'] };
        let serverOptions = {
            run: { module: serverModule, transport: vscode_languageclient_1.TransportKind.ipc },
            debug: { module: serverModule, transport: vscode_languageclient_1.TransportKind.ipc, options: debugOptions }
        };
        let documentSelector = ['css', 'scss', 'less'];
        let clientOptions = {
            documentSelector,
            synchronize: {
                configurationSection: ['css', 'scss', 'less']
            },
            initializationOptions: {},
            vscode: vscode
        };
        window.addEventListener("css_request_initialize", (ev) => {
            var detail = ev.detail.params;
            function hasClientCapability(name) {
                let keys = name.split('.');
                let c = detail.capabilities;
                for (let i = 0; c && i < keys.length; i++) {
                    c = c[keys[i]];
                }
                return !!c;
            }
            let snippetSupport = hasClientCapability('textDocument.completion.completionItem.snippetSupport');
            let capabilities = {
                textDocumentSync: 1,
                completionProvider: snippetSupport ? { resolveProvider: false } : undefined,
                hoverProvider: true,
                documentSymbolProvider: true,
                referencesProvider: true,
                definitionProvider: true,
                documentHighlightProvider: true,
                codeActionProvider: true,
                renameProvider: true,
                colorProvider: true
            };
            var cb = ev.detail["requests"].get("css_request_initialize_" + ev.detail.uuid);
            cb && cb({ capabilities });
        });
        let client = new vscode_languageclient_1.LanguageClient('css', 'CSS Language Server', serverOptions, clientOptions);
        client.registerFeature(new vsclient.ConfigurationFeature(client));
        let disposable = client.start();
        context.subscriptions.push(disposable);
        let indentationRules = {
            increaseIndentPattern: /(^.*\{[^}]*$)/,
            decreaseIndentPattern: /^\s*\}/
        };
        vscode.languages.setLanguageConfiguration('css', {
            wordPattern: /(#?-?\d*\.\d\w*%?)|(::?[\w-]*(?=[^,{;]*[,{]))|(([@#.!])?[\w-?]+%?|[@#!.])/g,
            indentationRules: indentationRules
        });
        vscode.languages.setLanguageConfiguration('less', {
            wordPattern: /(#?-?\d*\.\d\w*%?)|(::?[\w-]+(?=[^,{;]*[,{]))|(([@#.!])?[\w-?]+%?|[@#!.])/g,
            indentationRules: indentationRules
        });
        vscode.languages.setLanguageConfiguration('scss', {
            wordPattern: /(#?-?\d*\.\d\w*%?)|(::?[\w-]*(?=[^,{;]*[,{]))|(([@$#.!])?[\w-?]+%?|[@#!$.])/g,
            indentationRules: indentationRules
        });
        const regionCompletionRegExpr = /^(\s*)(\/(\*\s*(#\w*)?)?)?$/;
        vscode.languages.registerCompletionItemProvider(documentSelector, {
            provideCompletionItems(doc, pos) {
                let lineUntilPos = doc.getText(new vscode.Range(new vscode.Position(pos.line, 0), pos));
                let match = lineUntilPos.match(regionCompletionRegExpr);
                if (match) {
                    let range = new vscode.Range(new vscode.Position(pos.line, match[1].length), pos);
                    let beginProposal = new vscode.CompletionItem('#region', vscode.CompletionItemKind.Snippet);
                    beginProposal.range = range;
                    vscode.TextEdit.replace(range, '/* #region */');
                    beginProposal.insertText = new vscode.SnippetString('/* #region $1*/');
                    beginProposal.documentation = 'Folding Region Start';
                    beginProposal.filterText = match[2];
                    beginProposal.sortText = 'za';
                    let endProposal = new vscode.CompletionItem('#endregion', vscode.CompletionItemKind.Snippet);
                    endProposal.range = range;
                    endProposal.insertText = '/* #endregion */';
                    endProposal.documentation = 'Folding Region End';
                    endProposal.sortText = 'zb';
                    endProposal.filterText = match[2];
                    return [beginProposal, endProposal];
                }
                return null;
            }
        });
        window.addEventListener("css_request_" + vscode_languageserver_protocol_1.CompletionRequest.type.method, (ev) => {
            var textDocumentPosition = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(textDocumentPosition.textDocument.uri.toString());
                let stylesheet = stylesheets.get(document);
                return getLanguageService(document).doComplete(document, textDocumentPosition.position, stylesheet);
            }, null, `在为 ${textDocumentPosition.textDocument.uri} 执行智能提示时发生错误`, ev.detail);
        });
        window.addEventListener("css_request_" + vscode_languageserver_protocol_1.HoverRequest.type.method, (ev) => {
            var textDocumentPosition = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(textDocumentPosition.textDocument.uri);
                let styleSheet = stylesheets.get(document);
                return getLanguageService(document).doHover(document, textDocumentPosition.position, styleSheet);
            }, null, `Error while computing hover for ${textDocumentPosition.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("css_request_" + vscode_languageserver_protocol_1.DocumentSymbolRequest.type.method, (ev) => {
            var documentSymbolParams = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(documentSymbolParams.textDocument.uri);
                let stylesheet = stylesheets.get(document);
                return getLanguageService(document).findDocumentSymbols(document, stylesheet);
            }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("css_request_" + vscode_languageserver_protocol_1.DefinitionRequest.type.method, (ev) => {
            var documentSymbolParams = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(documentSymbolParams.textDocument.uri);
                let stylesheet = stylesheets.get(document);
                return getLanguageService(document).findDefinition(document, documentSymbolParams.position, stylesheet);
            }, null, `Error while computing definitions for ${documentSymbolParams.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("css_equest_" + vscode_languageserver_protocol_1.DocumentHighlightRequest.type.method, (ev) => {
            var documentSymbolParams = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(documentSymbolParams.textDocument.uri);
                let stylesheet = stylesheets.get(document);
                return getLanguageService(document).findDocumentHighlights(document, documentSymbolParams.position, stylesheet);
            }, [], `Error while computing document highlights for ${documentSymbolParams.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("css_request_" + vscode_languageserver_protocol_1.ReferencesRequest.type.method, (ev) => {
            var referenceParams = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(referenceParams.textDocument.uri);
                let stylesheet = stylesheets.get(document);
                return getLanguageService(document).findReferences(document, referenceParams.position, stylesheet);
            }, [], `Error while computing references for ${referenceParams.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("css_request_" + vscode_languageserver_protocol_1.CodeActionRequest.type.method, (ev) => {
            var codeActionParams = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(codeActionParams.textDocument.uri);
                let stylesheet = stylesheets.get(document);
                return getLanguageService(document).doCodeActions(document, codeActionParams.range, codeActionParams.context, stylesheet);
            }, [], `Error while computing code actions for ${codeActionParams.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("css_request_" + vsProto.ColorPresentationRequest.type.method, (ev) => {
            var params = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(params.textDocument.uri);
                if (document) {
                    let stylesheet = stylesheets.get(document);
                    return getLanguageService(document).getColorPresentations(document, stylesheet, params.color, params.range);
                }
                return [];
            }, [], `Error while computing color presentations for ${params.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("css_request_" + vscode_languageserver_protocol_1.RenameRequest.type.method, (ev) => {
            var renameParameters = ev.detail.params[0];
            runSafe(() => {
                let document = documents.get(renameParameters.textDocument.uri);
                let stylesheet = stylesheets.get(document);
                return getLanguageService(document).doRename(document, renameParameters.position, renameParameters.newName, stylesheet);
            }, null, `Error while computing renames for ${renameParameters.textDocument.uri}`, ev.detail);
        });
        window.addEventListener("css_request_" + vsProto.DocumentColorRequest.type.method, (ev) => {
            var params = ev.detail.params[0];
            return runSafe(() => {
                let document = documents.get(params.textDocument.uri);
                if (document) {
                    let stylesheet = stylesheets.get(document);
                    return getLanguageService(document).findDocumentColors(document, stylesheet);
                }
                return [];
            }, [], `Error while computing document colors for ${params.textDocument.uri}`, ev.detail);
        });
        vscode.commands.registerCommand('_css.applyCodeAction', applyCodeAction);
        function applyCodeAction(uri, documentVersion, edits) {
            let textEditor = vscode.window.activeTextEditor;
            if (textEditor && textEditor.document.uri.toString() === uri) {
                if (textEditor.document.version !== documentVersion) {
                    vscode.window.showInformationMessage(`CSS fix is outdated and can't be applied to the document.`);
                }
                textEditor.edit(mutator => {
                    for (let edit of edits) {
                        mutator.replace(asRange(edit.range), edit.newText);
                    }
                }).then(success => {
                    if (!success) {
                        vscode.window.showErrorMessage('Failed to apply CSS fix to the document. Please consider opening an issue with steps to reproduce.');
                    }
                });
            }
        }
    }
    exports.activate = activate;
});
